﻿using CipherMode = System.Security.Cryptography.CipherMode;

namespace Devonline.Entity;

/// <summary>
/// 机密, 密钥容器, 字符串类型的默认实现
/// </summary>
[Table("secret"), DisplayName("密钥")]
public class Secret : Secret<string>, IDateTimeRange, IEntitySet, IEntitySetWithCreate
{
    /// <summary>
    /// 构造方法给 Id 赋值
    /// </summary>
    public Secret() => Id = KeyGenerator.GetStringKey();

    /// <summary>
    /// 密钥加密证书
    /// </summary>
    public virtual Certificate? Certificate { get; set; }
    /// <summary>
    /// 通用附件集合, NotMapped Attachments 用于记录实体对象上上传的附件
    /// </summary>
    [NotMapped]
    public virtual ICollection<Attachment>? Attachments { get; set; }
}

/// <summary>
/// 机密, 密钥容器, 此处默认使用 Aes 256 CBC 模式, 用于具体数据, 文件等加密
/// 适用于 OwnerType 为: User, Role, Group, AuthorizeType 的情况下, 保存对应的密钥
/// 其余 OwnerType 类型不适用
/// 当 OwnerType != User 时, 角色, 组织和级别的 密钥 将同时由其所有用户公钥加密保存一份, 以便于用户可以使用自己的私钥, 解密并获取所属角色, 组织和级别的数据
/// 此时只保存所属角色, 组织和级别的 密钥 由当前用户公钥加密的密文
/// 特权用户由所有用户 密钥 的密钥备份, 以便于特权用户可以在用户忘记密码时帮助用户恢复数据
/// 字符编码算法使用 Base64
/// </summary>
/// <typeparam name="TKey"></typeparam>
[Table("secret"), DisplayName("密钥")]
public class Secret<TKey> : EntitySetWithCreate<TKey>, IDateTimeRange, IEntitySet<TKey>, IEntitySetWithCreate<TKey> where TKey : IConvertible
{
    /// <summary>
    /// 加密证书编号
    /// </summary>
    [Column("certificate_id"), DisplayName("加密证书编号"), Required, MaxLength(36)]
    public virtual TKey CertificateId { get; set; } = default!;
    /// <summary>
    /// 密钥所有者编号
    /// </summary>
    [Column("owner_id"), DisplayName("密钥所有者编号"), Required, MaxLength(36)]
    public virtual TKey OwnerId { get; set; } = default!;
    /// <summary>
    /// 密钥所有者身份类型 OwnerType 枚举值
    /// </summary>
    [Column("identity_type", TypeName = "varchar(16)"), DisplayName("密钥所有者身份类型"), DefaultValue(IdentityType.User)]
    public virtual IdentityType IdentityType { get; set; }
    /// <summary>
    /// 有效期始, 密钥激活时间
    /// </summary>
    [Column("start"), DisplayName("有效期始"), Field(Index = 8, Size = 16, Format = AppSettings.DEFAULT_FILE_DATE_FORMAT), Excel(Format = AppSettings.DEFAULT_DATE_FORMAT, Size = AppSettings.DEFAULT_EXCEL_COLUMN_WIDTH)]
    public virtual DateTime? Start { get; set; }
    /// <summary>
    /// 有效期至, 密钥过期时间
    /// </summary>
    [Column("end"), DisplayName("有效期至"), Field(Index = 9, Size = 16, Format = AppSettings.DEFAULT_FILE_DATE_FORMAT), Excel(Format = AppSettings.DEFAULT_DATE_FORMAT, Size = AppSettings.DEFAULT_EXCEL_COLUMN_WIDTH)]
    public virtual DateTime? End { get; set; }
    /// <summary>
    /// 散列算法
    /// </summary>
    [Column("hash_algorithm", TypeName = "varchar(16)"), DisplayName("散列算法"), DefaultValue(HashAlgorithm.SHA256)]
    public virtual HashAlgorithm HashAlgorithm { get; set; }
    /// <summary>
    /// 加密算法, 对称加密算法
    /// </summary>
    [Column("encryption_algorithm", TypeName = "varchar(16)"), DisplayName("加密算法"), DefaultValue(SymmetricAlgorithm.AES_256_CBC)]
    public virtual SymmetricAlgorithm EncryptionAlgorithm { get; set; }
    /// <summary>
    /// 密钥长度, 默认 256 位
    /// </summary>
    [Column("key_size"), DisplayName("密钥长度"), DefaultValue(256)]
    public virtual int KeySize { get; set; } = 256;
    /// <summary>
    /// 对称算法的运算模式
    /// </summary>
    [Column("cipher_mode", TypeName = "varchar(16)"), DisplayName("运算模式"), DefaultValue(CipherMode.CBC)]
    public virtual CipherMode CipherMode { get; set; } = CipherMode.CBC;
    /// <summary>
    /// 哈希值, 保存为明文, 用户密钥的明文签名, 用于校验用户密钥解密后的正确性
    /// 此值保存 KeyIV.Value 值的 HashAlgorithm 计算结果的 Base64 编码字符串, 最大支持 SHA512
    /// </summary>
    [Column("hash_code"), DisplayName("哈希值"), Required, MaxLength(128)]
    public virtual string HashCode { get; set; } = null!;
    /// <summary>
    /// 此字段保存 Aes 算法的密钥和初始化向量的密文
    /// 此值保存 KeyIV.Value 值的密文的 Base64 编码字符串
    /// 对称算法密钥, 此密钥是加密用户数据的关键密钥, 保存为密文 Base64 编码字符串, 由用户公钥加密, 用户私钥解密
    /// </summary>
    [Column("content"), DisplayName("密钥"), Required, MaxLength(512)]
    public virtual string Content { get; set; } = null!;
    /// <summary>
    /// 文件地址, 相对地址加上文件名, 密钥若是已文件形式保存, 这是保存地址
    /// </summary>
    [Column("path"), DisplayName("文件地址"), MaxLength(255)]
    public virtual string? Path { get; set; }
}